﻿using System;
using System.IO;
using System.Reflection;
using System.Runtime.Loader;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;

namespace FFmpegVideoClip
{
    static class Program
    {
        /// <summary>
        /// Application Entry Point.
        /// </summary>
        [System.STAThreadAttribute()]
        static void Main(string[] args)
        {
            AssemblyLoadContext.Default.Resolving += ResolveAssembly;
            if (IsUpdateDll())
                return;
            System.Threading.ThreadPool.GetMinThreads(out int workerTreads, out int completionPortThreads);
            System.Threading.ThreadPool.SetMinThreads(workerTreads > 20 ? 20 : workerTreads, completionPortThreads > 16 ? 16 : completionPortThreads);
            Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
            StringBuilder sb = new StringBuilder();
            foreach (string item in Directory.GetDirectories(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Tools"), "*", SearchOption.AllDirectories))
                sb.Append(item + ";");
            Environment.SetEnvironmentVariable("PATH", sb.ToString() + Environment.GetEnvironmentVariable("PATH"), EnvironmentVariableTarget.Process);
            if (File.Exists(logFile))File.Delete(logFile);
            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
            TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
            FFmpegVideoClip.App app = new FFmpegVideoClip.App();
            app.InitializeComponent();
            Application.Current.DispatcherUnhandledException += Current_DispatcherUnhandledException;
            app.Run();
            foreach (var ps in ProcessShell.pss)
                ps.Kill();
        }
        private static string logFile = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "ErrorLog.log");
        private static void WriteException(Exception? exception)
        {
            if (exception == null)
                return;
            StringBuilder stringBuilder = new StringBuilder();
            while (exception != null)
            {
                stringBuilder.AppendLine(exception.Message);
                stringBuilder.AppendLine(exception.StackTrace);
                exception = exception.InnerException;
            }
            if (!File.Exists(logFile))
                using (FileStream fs = File.Create(logFile)) { }
            File.AppendAllText(logFile, stringBuilder.ToString());
        }
        private static void TaskScheduler_UnobservedTaskException(object? sender, UnobservedTaskExceptionEventArgs e) => WriteException(e.Exception);
        private static void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e) => WriteException(e.Exception);
        private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e) { if (e.ExceptionObject is Exception ex) WriteException(ex); }
        private static Assembly ResolveAssembly(AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName)
        {
            if(AppContext.GetData("SubdirectoriesToProbe") is not string probeSetting)
                return null!;
            foreach (string subDirectory in probeSetting.Split(';'))
            {
                string pathMaybe = System.IO.Path.Combine(AppContext.BaseDirectory, subDirectory, $"{assemblyName.Name}.dll");
                if (File.Exists(pathMaybe))
                {
                    return assemblyLoadContext.LoadFromAssemblyPath(pathMaybe);
                }
            }

            return null!;
        }
    
        private static bool IsUpdateDll()
        {
            string path = Assembly.GetAssembly(typeof(Program))!.Location;
            string dir = Path.GetDirectoryName(path)!;
            string FName = Path.GetFileName(path);
            string OldPath = Path.Combine(dir, $"{FName}.old");
            string UpdatePath = Path.Combine(dir, $"{FName}.Update");
            if (File.Exists(OldPath))
                File.Delete(OldPath);

            if(File.Exists(UpdatePath))
            {
                File.Move(path, OldPath);
                File.Move(UpdatePath, path);
                System.Windows.Forms.Application.Restart();
                return true;
            }
            return false;
        }
    }
}
